![Snowflakeが提供するガバナンス機能のまとめ](https://devio2023-media.developers.io/wp-content/uploads/2021/03/snowflake-logo-1200x630-1.png)
Snowflakeが提供するガバナンス機能のまとめ
DA事業部コンサルティングチームのnkhrです。
センシティブなデータ(一部の人のみ参照可能)を扱う場合に利用できるSnowflakeのガバナンス機能を整理しました。本ブログでは、以下の内容を記載しています。
Snowflakガバナンス機能の概要
多くの人がデータを利用できる環境においては、データプライバシーや重要情報などの観点から、誰がどのデータまで参照してよいかを考える必要があります。本ブログでは、データ参照権限の制御や追跡をサポートするSnowflake機能をまとめました。
- 2021/11時点の機能や制約のため、最新の制約については公式サイトを参照してください)
- 本ブログではSnowflakeが提供している機能にのみフォーカスしています。広い意味でのデータガバナンス(体制や戦略など)については記載していません。
機能を利用する場合の注意点は、テーブルサイズが大きい場合、Maskingやアクセスポリシー機能を利用すると、クエリ実行時のパフォーマンス(Credits消費)に影響する場合があります。また、機能自体の制約事項も考慮する必要があります。機能の利用は、要件や制約および、性能のトレードオフを踏まえて検討する必要があります。
Dynamic Data Masking
カラム(列)ベースのセキュリティ機能で、masking policyによりテーブルまたはビューの列値を動的にマスキングします。(権限のある場合のみ値が見え、それ以外は指定した値でマスキングされます)
以下に特徴を記載します。
- マスキングの元値と、マスク後の値は同じdata typeとする必要がある(Timetamp型の列をマスキングする場合は、マスキング後の値もTimestamp型:00:00:00T00:00:00とする)
- 参照可能なRoleでMaskingされた列値を取得(SELECT)し、Maskingの設定のないテーブルや列に書きだした場合、Masking設定は引き継がれない
- MaskingされたTableを対象にしたビューには、テーブルのMaking policyが適用される
- クエリ実行時に各カラムを評価する(Makingの対象カラムが多いと処理に影響)
- Enterprise plan以上が必要
External Tokenization
カラム(列)ベースのセキュリティ機能で、対象カラムの値をtokenizationします。(センシティブな情報を含むデータ<トークン>をランダムな値に変換する)。こちらの機能を利用する場合は、Third-party製品の関数が必要です。
以下に特徴を記載します。
- Tokenizationは、Third-party Tokenization providerのREST APIを利用するため、外部関数(External function)の実行により実現(TokenizationはSnowflake外で行われる)
- データシェアリングでは利用できない
- 特定の文字列ごとに一意の値を生成するため、値はマスクしたいが、件数(COUNT)や別列のMAX値を集計したい場合の利用に適している(Dynamic Maskingでこの要件を実現するためには、Group別でCASE文を書く必要がある。マスク値のハッシュ化は可能)
- クエリ実行時に各カラムを評価(Makingの対象カラムが多いと処理に影響)
- 自分たちの利用したいTokenization providerを適用する場合はEnterprise plan以上が必要
Row Access Policy
Row Access Policyは、行レベルで表示・非表示を制御する機能です。Mappingテーブルを使ったPolicy定義と、単純なCASE文によるPolicy定義ができます。
以下に特徴を記載します。
- Policyは、オブジェクトとして独立しているため、同じPolicyが必要な複数のTableやViewに設定できる(例えば3つのテーブルすべてにregionカラムが存在し、特定Roleは特定Regionのみ参照可の場合、3つのテーブルで同じPolicyを利用できる)
- Snowflakeが内部でSecure viewを作成するため、Secure Viewと同様に実行時のパフォーマンスに影響あり
- 大きいテーブルの場合は、Policyで利用する属性をClustering Keyに設定しておくことでパフォーマンス向上が見込める
- Snowflakeはmetadataでテーブルの件数や大小を管理しているため、通常はCOUNTやMAXの値取得が高速に行える。しかしながら、Row Access policyを設定している場合、metadataで管理しているCOUNTやMAX値が使えないため高速な取得ができない
- Row access policyとColumn-level Masking policyは、テーブル・ビューに同時に設定できるが、Row access policyが先に評価される
- Row access policyが設定されているテーブルやビューからマテリアライズドビューは作成できない。設定されてないテーブル、ビューから作成されているマテリアライズドビューには、Row access policyを設定できる
- データシェアリングで利用できる
- Enterprise plan以上が必要
Secure Views
Row Access Policyで内部的に利用されている機能で、独立して利用する場合は、CREATE VIEWまたはCREATE MATERIALIZED VIEWコマンドのキーワードでsecureを設定します。権限のないユーザに対してDDL設定などを隠すことができます。
以下に特徴を記載します。
- Viewが一部のデータをフィルター(WHERE句で絞り込み)を行っている場合、Secureを付与しているViewのDDL定義は表示されない(通常のViewはできる)
select get_ddl('schema', 'test_schema');
コマンドは、Schema配下のテーブルやViewのDDL定義一覧を表示する。WHERE句で絞り込みを行っているViewの場合、通常のViewはDDLが表示されるが、Secure ViewはDDLが表示されない
- オプティマイザの評価時に、先にユーザの認可の記述を評価(ユーザのフィルター記述を先に評価することによるエラー発生を防ぐ)
- データシェアリングで利用できる
- Clustering Keyの利用を推奨
Secure UDF
UDFに対してsecureキーワードを設定することで実現します。
- 権限のあるRoleまたはOwner以外に対して、UDF詳細表示(GET_DDLやSHOW FUNCTIONS、Query Profile結果など)を抑制する
- Secure UDFの場合、通常のSQL UDFで行われるオプティマイザによる最適化をバイパスする(Secure UDFにすることでパフォーマンス影響が出る可能性がある)
Access History
ユーザやクエリが、どのデータをReadしたか記録したViewが提供されます。以前はRead Opeartionのみ記録されていましたが、2021/10から提供されているPreview機能を使うと、Write Operationも参照することができます。
Preview機能の適用状況は下記のクエリで確認できます。
-- 2021/10のPublic Previewが適用されているか確認(Enabledで提供済み) USE ROLE ACCOUNTADMIN; SELECT SYSTEM$BEHAVIOR_CHANGE_BUNDLE_STATUS('2021_10'); -- 2021/10のPublic Preview有効化 SELECT SYSTEM$ENABLE_BEHAVIOR_CHANGE_BUNDLE('2021_10');
特徴は以下の通りです。
- 監査やユーザとクエリ傾向のインサイト、利用していないデータ特定などのために利用できる
- 1年間保持される。1年以上の保持が必要な場合は、Clone等で別テーブルへ書き出しが必要
- Enterprise plan以上が必要
Object Tagging
オブジェクトに対してタグを付与する機能が提供されています。タグ自体もオブジェクトです。
特徴は以下の通りです。
- タグはSchemaレベルのオブジェクトとして存在する。タグのアタッチ先は以下の通り。
- Account Level Object(User, Role, Database, Warehouseのみ)
- Database Level Object(Schema)
- Schema Level Object(Table, External Table, View, Materialized View, Stageのみ)
- Columns
- タグはSnowflakeのオブジェクト階層(上図)と同様に継承される(Tableにタグを付与した場合は、Tableオブジェクトの子オブジェクトColumnにも継承される)
- Key/Value形式でValueは最大256文字で、1つのオブジェクトに紐づけられるタグ数は最大20
- タグはオブジェクトとして存在するため、同じタグオブジェクトを複数オブジェクトにアタッチできる
- タグはReplicationにおいても反映されるため、以下の場合はエラーとなる
- Enterprise plan以下の環境にタグ付きのオブジェクトをReplicationしようとする
- Replication対象のデータベースが、Replication対象外のデータベースのタグを参照している
- クローンによる複製においてもタグは維持される
- Snowflake.ACCOUNT_USAGEビューで作成タグを確認する場合は、更新まで1-3時間程度の遅延がある(ACCOUNT_USAGEビューは、Snowflake内部管理のmetadata storeからデータを抽出しているため、最新の情報が反映されるまでに通常1-3時間のタイムラグが発生する)
- TagはSchema-Levelのオブジェクトのため、利用にはTagの親となるDatabaseとSchemaに対するUsage権限が必要(Snowflakeのアクセスコントロ- Enterprise plan以上が必要
タグは、以下のような用途に利用することができます。
- タグは独立したオブジェクトのため、異なるテーブルのカラムや、複数のテーブルに同じタグを付与でき、異なるオブジェクトに同じ意味をもたせられる
- Credit集計において、Credit Viewテーブル(*_METERING_HISTORY)と結合することでタグベースのCredit集計ができる
- タグから、タグが付与されたオブジェクトをたどれるため、タグベースでオブジェクトのグルーピングができる
Dynamic Maskingサンプル
Dynamic Maskingはテーブルのカラムに設定します。Dynamic Maskingのサンプルコードは以下の設定を事前に行っています(コードに書いてない部分)
【実行の前提となるオブジェクトと権限設定】
- テーブル「test_db」とスキーマ「gov」の作成
- スキーマのowner role「shema_ower」の作成
- ウェアハウス「test_wh」の作成
- Maskingを管理するRole「masking_admin」の作成
- test_dbとgov、test_whへのUSAGE権限付与
- Maskingを検証するためのRole「test_role_sensitive_ok;」と「test_role_sensitive_ng」の作成
- test_dbとgov、test_whへのUSAGE権限付与
- 作成したテーブル「masking_test」へのSELECT権限付与
【補足事項】
- 「—」はコメントを表します
- ROLEオブジェクト名はcase-insensitiveだが、CURRENT_ROLE()はROLE名を大文字の文字列で返すため、Mapping Policyでは大文字のROLE名を指定する
-- MASKING POLICYを作成・アタッチするための権限付与 USE ROLE shema_ower; GRANT CREATE MASKING POLICY ON SCHEMA gov TO ROLE masking_admin; USE ROLE ACCOUNTADMIN; GRANT APPLY MASKING POLICY ON ACCOUNT TO ROLE masking_admin; -- Maskingのテスト用テーブル作成 USE ROLE shema_ower; CREATE OR REPLACE TABLE test_db.gov.masking_test ( id INTEGER, sensitive_data VARCHAR, sensitive_time TIMESTAMP, src VARIANT, encrypted_data BINARY) AS SELECT column1, column1, column3::timestamp_ntz, parse_json(column4), encrypt(column5, 'passphrase111') -- デフォルトはAES-256で暗号化される FROM VALUES (1, 'deny KAG group', '2021-10-01', '{"data": "sensitive"}', 'important data1'), (2, 'allow ABA group, CC group', '2021-10-01', '{"data":"important"}', 'important data2'), (3, 'deny MMMMM group', '2021-10-02', '{"data":"daiji"}', 'important data3') ; SELECT * FROM test_db.gov.masking_test; -- Masking policyの作成 USE ROLE masking_admin; CREATE OR REPLACE MASKING POLICY test_db.gov.sensitive_comment_mask_gen AS (val string) RETURNS string -> CASE -- ROLEオブジェクトは通常case-insensitiveだが、CURRENT_ROLE()はROLE名を大文字で返す WHEN CURRENT_ROLE() IN ('TEST_ROLE_SENSITIVE_OK') THEN val ELSE '*********' END ; -- テーブルにMasking Policyをアタッチ ALTER TABLE IF EXISTS test_db.gov.masking_test MODIFY COLUMN sensitive_data SET MASKING POLICY test_db.gov.sensitive_comment_mask_gen; -- 動作確認 USE ROLE test_role_sensitive_ok; SELECT * FROM TATENO_TEST_DB.gov.masking_test; -- >> Maskingされずに表示される USE ROLE test_role_sensitive_ng; SELECT * FROM TATENO_TEST_DB.gov.masking_test; -- >> Maskingされる
おまけ:Masking Policyの色々なパターン
-- 権限のないユーザにはHASH化した値を返す CREATE OR REPLACE MASKING POLICY test_db.gov.sensitive_comment_mask_sha2 AS (val string) RETURNS string -> CASE WHEN CURRENT_ROLE() IN ('TEST_ROLE_SENSITIVE_OK') THEN val ELSE sha2(val) END ; -- Timestamp型のカラムに対してマスキングを設定する CREATE OR REPLACE MASKING POLICY test_db.gov.sensitive_comment_mask_timesamp AS (val TIMESTAMP) RETURNS TIMESTAMP -> CASE WHEN CURRENT_ROLE() IN ('TEST_ROLE_SENSITIVE_OK') THEN val ELSE date_from_parts(0001, 01, 01)::timestamp_ntz END ; -- VARIANT型(JSON格納)の1つの属性をマスキングする -- object_insert(<VARIANT型のカラム名>, <属性名>, <変換する属性値>, <updateFlag>) -- updateFlagがTRUEの場合は、既存のキーを上書き/更新。デフォルトはFALSEで新しく挿入 CREATE OR REPLACE MASKING POLICY test_db.gov.sensitive_comment_mask_variant AS (val VARIANT) RETURNS VARIANT -> CASE WHEN CURRENT_ROLE() IN ('TEST_ROLE_SENSITIVE_OK') THEN val ELSE object_insert(val, 'data', '****', true) END ; -- 暗号化済みのカラムに対して権限のあるユーザのみ復号化した値を取得 CREATE OR REPLACE MASKING POLICY test_db.gov.sensitive_comment_mask_decrypt AS (val BINARY) RETURNS BINARY -> CASE WHEN CURRENT_ROLE() IN ('TEST_ROLE_SENSITIVE_OK') THEN decrypt(val, 'passphrase111') ELSE val END ; -- マスキングの有無を別テーブル(entitlement)に保持してマスキング時に動的に判定 -- マスキングポリシー変更なしで、マスキング/アンマスキング設定変更が可能だがパフォーマンスtrade-off CREATE OR REPLACE TABLE test_db.gov.entitlement (role_name VARCHAR, can_read BOOLEAN) AS SELECT column1, column2 FROM VALUES ('TEST_ROLE_SENSITIVE_OK', true), ('TEST_ROLE_SENSITIVE_NG', false) ; CREATE OR REPLACE MASKING POLICY test_db.gov.sensitive_comment_mask_usetable AS (val string) RETURNS string -> CASE WHEN EXISTS ( SELECT 1 FROM test_db.gov.entitlement WHERE can_read and role_name = CURRENT_ROLE() ) THEN val ELSE '*********' END ;
Access Historyサンプル
Access Historyは以下のクエリで取得できます。サイズが大きいのでキーで絞るかLIMITを使うとよいと思います。
USE WAREHOUSE <warehouse name>; -- ABC_Userの履歴(最新履歴10件取得) SELECT * FROM SNOWFLAKE.ACCOUNT_USAGE.access_history WHERE USER_NAME = 'ABC_User' ORDER BY query_start_time DESC LIMIT 10; -- WriteOperationのAccount History取得(最新履歴10件取得) SELECT * FROM SNOWFLAKE.ACCOUNT_USAGE.access_history WHERE ARRAY_SIZE(OBJECTS_MODIFIED) != 0 ORDER BY query_start_time DESC LIMIT 10;
ObjectTaggingサンプル
テーブルやカラムへのタグの付与サンプルです。
【実行の前提となるオブジェクトと権限設定】
- テーブル「test_db」とスキーマ「gov」の作成
- スキーマのowner role「shema_ower」の作成
- ウェアハウス「test_wh」の作成
- タグを管理するRole「tag_admin」の作成
- test_dbとtest_whへのUSAGE権限付与
- スキーマgovのUSAGEとCREATE TABLE権限付与
-- タグの作成とアタッチ権限を付与 USE ROLE shema_ower; GRANT CREATE TAG ON SCHEMA gov TO ROLE tag_admin; USE ROLE ACCOUNTADMIN; GRANT APPLY TAG ON ACCOUNT TO ROLE tag_admin; -- タグを作成 USE ROLE tag_admin; CREATE TAG test_db.gov.usage_type; CREATE TAG test_db.gov.data_type; CREATE TAG test_db.gov.env; -- スキーマ内のタグ一覧を表示 SHOW TAGS IN SCHEMA test_db.gov; -- テーブルとカラムにタグを付与 USE ROLE tag_admin; CREATE OR REPLACE TABLE test_db.gov.test ( id Integer, v1 VARCHAR WITH TAG data_type = 'sensitive' ) WITH TAG (usage_type = 'personal', env = 'test') AS SELECT column1, column2 FROM VALUES (1, 'aaa'), (2, 'bbb'), (3, 'ccc'); -- 削除済みのタグも含めてアカウントすべてのタグを表示する SELECT * FROM snowflake.account_usage.tags ORDER BY tag_name; -- タグとタグをアタッチしたオブジェクトの一覧を表示(どのタグがどのオブジェクトにアタッチされているか) -- domainはオブジェクトのタイプを表す('TABLE', 'COLUMN', 'WAREHOUSE'など) SELECT * FROM snowflake.account_usage.tag_references ORDER BY tag_name, domain, object_id
最後に
データガバナンスやセキュリティは、後回しにされる場合もありますが、扱うデータ量が増えれば増えるほど、後付けでの対応が大変になる部分かなと思います。機能の利用有無にかかわらず、データ分析基盤を検討していく中で、以下のようなことも意識するとよいかもしれません。
- 構築予定のデータ分析基盤は、どのようなレベルのデータを保持する環境か
- どのようなユーザがアクセスできる(する)環境か
- データポリシーの確認(個人情報、機密情報などレベルごとのデータ分類に従い、だれがどのレベルのデータまでアクセスしてよいか、どのような対応をすればアクセスできるかなどの方針の確認)
- 機密性の高いデータを取り込む場合に必要な対応の確認(マスキング済みデータのみ取り込むか、取り込んだ後にマスキングするか、その場合はどのようなアクセス管理が必要かなど)
- データ品質や信頼性、セキュリティを維持するための体制やプロセスの設計